package labcontrole;

/**
 * Classe que implementa as funcoes de controle da planta
 * Os tipos de controle implementados são: P, PI, PD, PID
 * (Falta implementar o PI-D)
 * @author victor
 */
public class Controlador {

  private String tipo;
  private double NivelTanqueAnterior;
  private double nivelT1, nivelT2;
  private double referencia,referenciaAnterior;
  private double Kp, Ki, Kd;
  private double erroAtual, erroAnterior, Ianterior;
  private double tempoDePico;
  private double tempoDeAcomod;
  private double overShoot;
  private double tempoDeSubida;
  private double valorAnterior;
  private double sensorAtual, sensorAnterior;
  private boolean flagAcomod;
  private boolean flagSubida;
  private int flagOvershoot;
  private boolean achouOvershoot, entrouReferencia;
  private double tempo;

  public Controlador() {
    tempo = 0;
    referenciaAnterior = referencia = 0;
    Kp = Kd = Ki = Ianterior = 0.0;
    valorAnterior = 0;
    tempoDeAcomod = 0;
    tempoDePico = 0;
    tempoDeSubida = -1.0;
    overShoot = 0.0;
    valorAnterior = -1;
    sensorAtual = sensorAnterior = 0;
  }

  public void setTempo(double tempo) {
    this.tempo = tempo;
  }

  public void setNivelT1(double nivelT1) {
    this.nivelT1 = nivelT1;
  }

  public void setNivelT2(double nivelT2) {
    this.nivelT2 = nivelT2;
  }

  public void setTipo(String tipo) {
    this.tipo = tipo;
  }

  public void setReferencia(double referencia) {
    this.referencia = referencia;
  }
   public void setReferenciaAnterior(double referencia) {
    this.referenciaAnterior = referencia;
  }

  public double getReferencia() {
    return referencia;
  }
  public double getReferenciaAnterior() {
    return referenciaAnterior;
  }

  public void setKp(double Kp) {
    this.Kp = Kp;
  }

  public void setKd(double Kd) {
    this.Kd = Kd;
  }

  public void setKi(double Ki) {
    this.Ki = Ki;
  }

  public void setInfo(double erroAtual, double erroAnterior, double sensorAtual) {
    this.erroAtual = erroAtual;
    this.erroAnterior = erroAnterior;
    this.sensorAtual = sensorAtual;
  }

  private double P() {
    return Kp * erroAtual;
  }

  private double I() {
    Ianterior = Ianterior + Ki * erroAtual * 0.1;
    return Ianterior;
  }

  private double D() {
    return ((erroAtual - erroAnterior) / 0.1) * Kd;
  }

  private double _D() {
    double result = ((sensorAtual - sensorAnterior) / 0.1) * Kd;
    sensorAnterior = sensorAtual;
    return result;
  }

  public double getSinalControle() {
    if (tipo.equals("P")) {
      return P();
    } else if (tipo.equals("PI")) {
      return (P() + I());
    } else if (tipo.equals("PD")) {
      return (P() + D());
    } else if (tipo.equals("PID")) {
      return (P() + I() + D());
    } else if (tipo.equals("PI-D")) {
      return (P() + I() + _D());
    } else {
      return 0;
    }
  }

  /**
   * @param controlador identifica o tipo de controlador
   * @return acao de controle correspondente ao tipo de controlador
   */
  private void calcularTempoDeAcomod(double nivelTanque) {
    if ((nivelTanque <= (referencia + referencia * 0.02))
            && (nivelTanque >= (referencia - referencia * 0.02)) && !flagAcomod) {
      tempoDeAcomod = tempo;
      flagAcomod = true;
    }

    else if (!((nivelTanque <= (referencia + referencia * 0.02))
            && (nivelTanque >= (referencia - referencia * 0.02)))) {
      flagAcomod = false;
    }
  }

  private void calcularTempoSubida(double nivelTanque) {
    //Subindo
    if(referencia > referenciaAnterior){
        if (nivelTanque <= referencia && !flagSubida) {
          tempoDeSubida = tempo;
        } 
        else if (nivelTanque > referencia) {
          flagSubida = true;
        }
    }
    //Descendo
    if(referencia < referenciaAnterior){
         if ((nivelTanque*0.02) >= referencia && !flagSubida) {
          tempoDeSubida = tempo;
        } 
        else if (nivelTanque < referencia) {
          flagSubida = true;
        }
    }
  }

  private void calcularOvershooteTempoPico(double nivelTanque) {
          //Subindo
          if(referencia > referenciaAnterior){
              //Entrou na area de pico
              if(nivelTanque > referencia){
                  if(nivelTanque >= overShoot && !achouOvershoot){
                      overShoot = nivelTanque;
                      tempoDePico = tempo;
                      entrouReferencia = true;
                  }                  
              }
              //Saiu da área de pico
              else if(nivelTanque < referencia && entrouReferencia){
                  this.achouOvershoot = true; 
              }
          }
          //Descendo
          else if(referencia < referenciaAnterior){ 
              //Entrou na area de pico
              if(nivelTanque < referencia){                   
                  if(nivelTanque <= overShoot && !achouOvershoot){
                      overShoot = nivelTanque;
                      tempoDePico = tempo;
                      entrouReferencia = true;
                  }                   
              }
              //Saiu da área de pico
              else if(nivelTanque > referencia && entrouReferencia){
                  this.achouOvershoot = true; 
              }
          }
  }

  /**
   * Metodo utilizado para esvaziar o tanque
   * @param nivel atual do tanque
   * @return retorna a tensao necessaria para esvaziar o tanque
   */
  public double esvaziar(double nivel) {
    if (nivel > 6.0) {
      return -3.0;
    } else {
      return 0.0;
    }
  }

  public Double getTempoPico(double nivelTanque) {
    calcularOvershooteTempoPico(nivelTanque);
    return this.tempoDePico;
  }

  public Double getOverShoot(double nivelTanque) {
    calcularOvershooteTempoPico(nivelTanque);
    return this.overShoot;
  }

  public Double getTempoAcomod(double nivelTanque) {
    calcularTempoDeAcomod(nivelTanque);
    return this.tempoDeAcomod;
  }

  public Double getTempoSubida(double nivelTanque) {
    calcularTempoSubida(nivelTanque);
    return this.tempoDeSubida;
  }

  public double getOverShoot() {
    return overShoot;
  }

  public void setOverShoot(double overShoot) {
    this.overShoot = overShoot;
  }
  public boolean getAchouOvershoot(){
      return this.achouOvershoot;
  }
  public void setAchouOvershoot(boolean achou){
      this.achouOvershoot = achou;
      this.entrouReferencia = achou;
  }
  public void setFlagSubida(boolean achou){
      this.flagSubida = achou;
  }
  public boolean getFlagSubida(){
      return this.flagSubida;
  }
  public void setTempoDeSubida(double x){
      this.tempoDeSubida = x;
  }
  public void setTempoDeAcomod(double x){
      this.tempoDeAcomod = x;
  }
}
